package me.gavin.base

import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import androidx.core.content.ContextCompat
import androidx.databinding.ObservableArrayList
import androidx.fragment.app.Fragment
import java.io.Serializable
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

fun Context.getColorCompat(colorRes: Int) = ContextCompat.getColor(this, colorRes)
fun Context.getColorStateListCompat(colorRes: Int) = ContextCompat.getColorStateList(this, colorRes)
fun Context.getDrawableCompat(drawableRes: Int) = ContextCompat.getDrawable(this, drawableRes)
fun Context.getStringArrayCompat(vararg res: Int) = res.map { getString(it) }

inline fun <reified T> Activity.argument(key: String/*, defVal: T*/) = lazy { // todo
    intent.extras?.get(key) as T /*?: error("Intent argument $key is missing")*/
}
inline fun <reified T> Activity.argument(key: String, defVal: T) = lazy { // todo
    intent.extras?.get(key) as? T ?: defVal
}

fun Activity.pageType() = lazy { intent.type }

inline fun <reified T> Fragment.argument(key: String) = lazy {
    arguments?.get(key) as T
}

fun Bundle.fillArgs(vararg params: Pair<String, Any?>) = apply {
    params.forEach {
        when (val value = it.second) {
            null -> putSerializable(it.first, null as Serializable?)
            is Int -> putInt(it.first, value)
            is Long -> putLong(it.first, value)
            is CharSequence -> putCharSequence(it.first, value)
            is String -> putString(it.first, value)
            is Float -> putFloat(it.first, value)
            is Double -> putDouble(it.first, value)
            is Char -> putChar(it.first, value)
            is Short -> putShort(it.first, value)
            is Boolean -> putBoolean(it.first, value)
            is Serializable -> putSerializable(it.first, value)
            is Bundle -> putBundle(it.first, value)
            is Parcelable -> putParcelable(it.first, value)
            is Array<*> -> when {
                value.isArrayOf<CharSequence>() -> putSerializable(it.first, value)
                value.isArrayOf<String>() -> putSerializable(it.first, value)
                value.isArrayOf<Parcelable>() -> putSerializable(it.first, value)
                else -> error("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
            }
            is IntArray -> putIntArray(it.first, value)
            is LongArray -> putLongArray(it.first, value)
            is FloatArray -> putFloatArray(it.first, value)
            is DoubleArray -> putDoubleArray(it.first, value)
            is CharArray -> putCharArray(it.first, value)
            is ShortArray -> putShortArray(it.first, value)
            is BooleanArray -> putBooleanArray(it.first, value)
            else -> throw error("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
        }
    }
}

fun <T: Fragment> T.withArgs(vararg params: Pair<String, Any?>) = apply {
    (arguments ?: Bundle().also { arguments = it }).fillArgs(*params)
}

infix fun Int.contain(flag: Int) = this and flag == flag

infix fun Int.containNot(flag: Int) = !contain(flag)

infix fun Int.add(flag: Int) = this or flag

infix fun Int.remove(flag: Int) = this and flag.inv()


fun <T> observableListOf(vararg elements: T) = ObservableArrayList<T>().also {
    if (elements.isNotEmpty()) it.addAll(elements)
}

inline fun <reified T> Any?.cast(): T? = this as? T
inline fun <reified T> Any?.castOrThrow(): T = this as T

inline fun <T> T.takeOrElse(predicate: Boolean, block: (T) -> T): T {
    return if (predicate) this else block(this)
}
inline fun <T> T.takeOrElse(predicate: (T) -> Boolean, block: (T) -> T): T {
    return takeOrElse(predicate(this), block)
}